/*-*-C-*-
 *
 * ResEd
 */

#include "resed.h"

static unsigned int eventmask = (BIT(EV_NULL_REASON_CODE) |
                                 BIT(EV_POINTER_LEAVING_WINDOW) |
                                 BIT(EV_POINTER_ENTERING_WINDOW) );
static MenuPtr iconmenu = NULL;
static WindowPtr infowin = NULL;
static WindowPtr exitwarnwin = NULL;


#define ICONMENU_INFO 0
#define ICONMENU_PALETTE 1
#define ICONMENU_QUIT 2


/*
 * Determine stuff about the screen mode
 */

static error * examine_mode (Bool checkfonts)
{
    ER ( swi (OS_ReadModeVariable,  R0, -1,  R1, 4,  OUT, R2, &scalex,  END) );
    ER ( swi (OS_ReadModeVariable,  R0, -1,  R1, 5,  OUT, R2, &scaley,  END) );
    scalex = 1 << scalex;
    scaley = 1 << scaley;
    dprintf("MODE %d by %d\n" _ scalex _ scaley);
    if (checkfonts)
        template_refind_fonts ();
    return NULL;
}


/*
 * Safe exit.
 */

static int sender = 0;
static unsigned int prequitflags;

static error * exit_interactor (unsigned int event, int *buf, void *closure, Bool *consumed)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    WindowPtr win = (WindowPtr) buf;         /* only half there */
    MessagePtr mess = (MessagePtr) buf;

    if (buf == NULL)                     /* we are being asked to cancel */
    {
        swi (Wimp_CreateMenu,  R1, -1,  END);
        return NULL;
    }

dprintf("in exit_interactor %d\n" _ event);

    switch (event)
    {
    case EV_OPEN_WINDOW_REQUEST:
        if (win->handle == exitwarnwin->handle)
        {
            *consumed = TRUE;
            return swi (Wimp_OpenWindow, R1, win, END);
        }
        break;

    case EV_MOUSE_CLICK:
        if (mouse->windowhandle == exitwarnwin->handle)
        {
            *consumed = TRUE;
            switch (mouse->iconhandle)
            {
            case I_EXITWARN_DISCARD:
                interactor_cancel ();
                /* If it was a PreQuit message from a ShutDown, then continue with that protocol */
                if (sender != 0 && (prequitflags & BIT(0)) == 0)
                {
                    KeyPressRec key;
dprintf("SENDING\n");
                    document_free_all ();
                    ER ( swi (Wimp_GetCaretPosition,  R1, &key.caret,  END) );
                    key.code = 0x1FC;     /* CTRL-SHIFT-F12 */
                    ER ( swi (Wimp_SendMessage,  R0, EV_KEY_PRESSED,  R1, &key,  R2, sender,  END) );
                    return NULL;
                }
dprintf("GONE\n");
                error_exit (0);
                /* NOTREACHED */
            case I_EXITWARN_CANCEL:
                interactor_cancel ();
            }
        }
        break;

    case EV_USER_MESSAGE:
    case EV_USER_MESSAGE_RECORDED:
        if (mess->code == 0x400c9 && buf[5] == exitwarnwin->handle)
        {
            interactor_cancel();
            *consumed = TRUE;
        }
        break;
    }

    return NULL;
}


/*
 * If msg is NULL, the user is trying to quit.  If it's non-zero,
 * then it is the task handle of the task which is trying to perform
 * a pre-quit.
 */

static error * warn_exit (MessagePreQuitPtr prequit)
{
    int num = 0, i = 0;
    RegistryType type;
    void *closure;
    char *s = dbox_getstring (exitwarnwin, I_EXITWARN_MESSAGE);

    if (prequit)
    {
        sender = prequit->header.taskhandle;
        prequitflags = prequit->header.size > 20 ? prequit->flags : 0;
dprintf("sender %d PQF %d\n" _ sender _ prequitflags);
    }
    else
        sender = 0;

    while ((i = registry_enumerate_windows (i, &type, NULL, &closure)) != 0)
    {
        if (type == Document)
        {
            DocumentPtr doc = (DocumentPtr) closure;
            if (doc->modified)
                num++;
        }
    }

    if (num == 0)
    {
        if (prequit)
            return NULL;
        else
            error_exit (0);
    }

    /* If it's a PreQuit, then halt the protocol */
    if (prequit)
    {
        prequit->header.yourref = prequit->header.myref;
        ER ( swi (Wimp_SendMessage,  R0, EV_USER_MESSAGE_ACKNOWLEDGE,  R1, prequit,  R2, sender,  END) );
    }

    if (exitwarnwin->handle <= 0)
    {
        ER ( swi (Wimp_CreateWindow,  R1, &exitwarnwin->visarea,
                  OUT,  R0, &exitwarnwin->handle,  END) );
    }
    sprintf (s, message_lookup (&msgs, num == 1 ? "ExitWarnS" : "ExitWarnP"), num);
    dbox_setstring (exitwarnwin, I_EXITWARN_MESSAGE, s);
    interactor_install (exit_interactor, NULL);
    ER ( swi (Wimp_CreateMenu,  R1, exitwarnwin->handle,
              R2, exitwarnwin->visarea.minx,  R3, exitwarnwin->visarea.maxy,  END) );
    return NULL;
}
    

/*
 * Handling the icon menu
 */

static error * create_iconmenu ()
{
    ER ( menu_create (3, message_lookup (&msgs, "IconMenu"), &iconmenu) );
    ER ( menu_entry (iconmenu, ICONMENU_INFO, message_lookup (&msgs, "Info"),
                     0, 0, -1, -1, (void *) infowin->handle) );
    ER ( menu_entry (iconmenu, ICONMENU_PALETTE, message_lookup (&msgs, "Palette"),
                     0, 0, -1, -1, NULL) );
    ER ( menu_entry (iconmenu, ICONMENU_QUIT, message_lookup (&msgs, "Quit"),
                     0, 0, -1, -1, NULL) );
    return NULL;
}


/*
 * Callback for the Icon Menu
 */

static error * iconmenu_cb (MenuPtr menu, int *buf, void *closure, Bool adjust)
{
    if (buf)
        switch (*buf)
        {
        case ICONMENU_PALETTE:
            palette->window.behind = -1;
            if (palette->window.handle <= 0)
            {
                /* Create window */
                palette->window.flags &= ~(WF_AUTO_REDRAW | WF_BACKDROP);
                palette->window.workareaflags = 10 << 12;

                ER ( swi (Wimp_CreateWindow,  R1, &palette->window.visarea,
                          OUT,  R0, &palette->window.handle,  END) );
                ER ( registry_register_window(palette->window.handle, Template, (void *) palette) );
            }

            /* Raise */
            ER ( swi (Wimp_OpenWindow,  R1, &palette->window,  END) );
            break;

        case ICONMENU_QUIT:
            return warn_exit (NULL);
        }
    return NULL;
}


/*
 * Respond to mouse click events in any window
 */

static error * mouse_click (int *buf)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    unsigned int modifiers = wimp_read_modifiers ();

    if (mouse->windowhandle == -2)
    {
        DocumentPtr newdoc;
        switch (mouse->buttons)
        {
        case MB_CLICK(MB_SELECT):
            ER ( document_create(&newdoc, "") );
            break;
        case MB_CLICK(MB_MENU):
            ER ( menu_post (iconmenu, &mouse->position, TRUE, iconmenu_cb, NULL) );
            break;
        case MB_CLICK(MB_ADJUST):
            block
            {
                int fake = ICONMENU_PALETTE;
                ER ( iconmenu_cb (NULL, &fake, NULL, FALSE) );
            }
            break;
        }
    }
    else
    {
        void *closure;
        RegistryType type = registry_lookup_window(mouse->windowhandle, &closure);
        switch (type)
        {
        case Document:
            ER ( document_mouse_click (mouse, modifiers, (DocumentPtr) closure) );
            break;
        case Template:
            ER ( template_mouse_click (mouse, modifiers, (ResourcePtr) closure) );
            break;
        case Winflags:
            ER ( winflags_mouse_click (mouse, modifiers) );
            break;
        case Grid:
            ER ( grid_mouse_click (mouse, modifiers) );
            break;
        case Extent:
            ER ( extent_mouse_click (mouse, modifiers) );
            break;
        case Props:
        case MoreProps:
            ER ( props_mouse_click (mouse, modifiers, (WindowPtr) closure) );
            break;
        case Colours:
            ER ( colours_mouse_click (mouse, modifiers) );
            break;
#if 0
        case Unknown:
        default:
            return error_lookup("UnkWin", "MOUSECLICK");
            break;
#endif
        }
    }
    return NULL;
}



/*
 * Handle keypresses.  ESCAPE cancels the current interactor.
 * Other keys may be handled by individual window types,
 * and unknown ones are passed back to the Wimp.
 */

static error * key_pressed (int *buf)
{
    KeyPressPtr key = (KeyPressPtr) buf;
    void *closure;
    Bool consumed = FALSE;

    if (key->code == 0x1b)
    {
        interactor_cancel ();
        consumed = TRUE;
    }
    else
    {
        switch (registry_lookup_window (key->caret.windowhandle, &closure))
        {
        case Document:
            ER ( document_key_pressed ((DocumentPtr) closure, key, &consumed) );
            break;
        case Template:
            ER ( template_key_pressed ((ResourcePtr) closure, key, &consumed) );
            break;
        case Winflags:
            ER ( winflags_key_pressed (key, &consumed) );
            break;
        case Grid:
            ER ( grid_key_pressed (key, &consumed) );
            break;
        case Extent:
            ER ( extent_key_pressed (key, &consumed) );
            break;
        case Props:
        case MoreProps:
            ER ( props_key_pressed (key, &consumed) );
            break;
        }
    }

    if (!consumed)
    {
        ER ( swi (Wimp_ProcessKey,  R0, key->code,  END) );
    }
    return NULL;
}


/*
 * Handle lose caret events
 */

static error * lose_caret (int *buf)
{
    CaretPositionPtr caret = (CaretPositionPtr) buf;
    void *closure;
    
    switch (registry_lookup_window (caret->windowhandle, &closure))
    {
    case Props:
    case MoreProps:
        ER ( props_lose_caret (caret) );
        break;
    }

    return NULL;
}


/*
 * Turn a help message around.  Help tags for most things are simply
 * determined by reference to the window type as held by the registry.
 */

static error * help_message (MessageHelpRequestPtr req)
{
    MessageHelpReplyPtr reply = (MessageHelpReplyPtr) req;
    int i;
    char tag[100], *value;

    for (i = 0; i < 2; i++)
    {
        if (req->mouse.windowhandle == -2 && req->mouse.iconhandle == iconhandle)       
            strcpy(tag, "IconHlp");
        else
        {
            RegistryType type = registry_lookup_window (req->mouse.windowhandle, NULL);
            if (type == Unknown)
                return NULL;
            else
            {
                char name[100];
                if (req->mouse.iconhandle != -1)
                {
                    IconStateRec state;
                    state.windowhandle = req->mouse.windowhandle;
                    state.iconhandle = req->mouse.iconhandle;
                    swi (Wimp_GetIconState,  R1, &state,  END);
                    if (template_read_icon_name (&state.icon, name) == FALSE)
                        strcpy (name, "WIN");
                }
                else
                    strcpy (name, "WIN");

                if (type == Props || type == MoreProps)
                    sprintf (tag, "Hlp%d.%d.%s", type, props_type(), name);
                else
                    sprintf(tag, "Hlp%d.%s", type, name);
            }
        }
        value = message_lookup (&msgs, tag);
        break;
    }

    /* Build reply in same memory as message */

    strcpy(reply->help, value);
    reply->header.yourref = req->header.myref;
    reply->header.messageid = MESSAGE_HELP_REPLY;
    reply->header.size = (sizeof(MessageHeaderRec) + strlen(reply->help) + 4) & ~3;
    return swi (Wimp_SendMessage,  NONX,
                R0, EV_USER_MESSAGE,
                R1, reply,
                R2, req->header.taskhandle,  END);
    return NULL;
}


/* DataSave: yourref should be == claimref (if 0 it's a regular DataSave)
             if claimref, then dragdrop_cancel ()
*/

/*
 * Someone is trying to do a DataSave to us.  If myref non-zero
 * then it's as a result of a dragdrop operation that we claimed.
 * If this is the case then the window-type-specific code
 * may need to remove ghost caret information, etc.
 */

static int claimwindow;
static int datasaveackref;

static error * datasave_message (MessageDataSavePtr req)
{
    dprintf("Recvd DATA_SAVE with yourref %d\n" _ req->header.yourref);

    claimwindow = -1;

    if (req->header.yourref != 0)
    {
        if (dragdrop_claimref())
            claimwindow = dragdrop_claimwin();
        dragdrop_cancel ();     /* causes window-specific code to undraw ghost caret etc */
    }

    switch (registry_lookup_window (req->windowhandle, NULL))
    {
    case Document:
    case Template:
        break;
    default:
        if (!(req->windowhandle == -2 && req->iconhandle == iconhandle))
            return NULL;
    }

    if (req->filetype != FILETYPE_TEMPLATE)
    {
        dprintf("Bounced file of type %d\n" _ req->filetype);
        return NULL;            /* bounce message */
    }

    req->header.yourref = req->header.myref;
    req->estsize = -1;          /* insecure destination */
    strcpy(req->leafname, "<Wimp$Scrap>");
    req->header.size = 256;
    req->header.messageid = MESSAGE_DATA_SAVE_ACK;
    ER ( swi (Wimp_SendMessage,
              R0, EV_USER_MESSAGE,
              R1, req,
              R2, req->header.taskhandle,  END) );
    datasaveackref = req->header.myref;

    return NULL;
}


/* Iconsprites the given sprite file, and redraw any Template windows
 * that might have been affected.
 */

static void iconsprites (char *filename)
{
    int i = 0;
    RegistryType type;
    void *closure;
    char buf[FILENAMELEN];

    sprintf (buf, "%%iconsprites %s", filename);
    system (buf);

    while ((i = registry_enumerate_windows (i, &type, NULL, &closure)) != 0)
    {
        if (type == Template)
        {
            ResourcePtr res = (ResourcePtr) closure;
            if (res->window.handle > 0)
            {
                RectRec work;
                wimp_convert_rect (ScreenToWork, &res->window, &res->window.visarea, &work);
                wimp_invalidate (&res->window, &work);
            }
        }
    }

    if (palette->window.handle > 0)
    {
        RectRec work;
        wimp_convert_rect (ScreenToWork, &palette->window, &palette->window.visarea, &work);
        wimp_invalidate (&palette->window, &work);
    }
}

    
/*
 * Respond to a DataLoad message, either from the Filer or as a result
 * of a dragdrop interaction.  If the yourref field equals the one
 * stored in datasaveackref, then claimwindow is valid unless -1.
 */

static error * dataload_message (MessageDataLoadPtr req)
{
    void *closure;
    Bool newfile = FALSE;

    if (req->filetype == FILETYPE_SPRITE)
    {
        iconsprites (req->filename);
    }
    else if (req->filetype == FILETYPE_TEMPLATE)
    {
        /* Determine where the load is going */

        if (req->windowhandle == -2 && req->iconhandle == iconhandle)
        {
            /* Load to a new document.  Really oughtn't open window till we finish operation */
            DocumentPtr newdoc;
            DocumentPtr olddoc = document_lookup_by_filename (req->filename);
            if (olddoc)
            {
                document_raise_window (olddoc);
                return NULL;
            }
            ER ( document_create(&newdoc, req->filename) );
            req->windowhandle = newdoc->window.handle;
            req->iconhandle = -1;
            newfile = TRUE;
        }

        switch (registry_lookup_window (req->windowhandle, &closure))
        {
        case Document:
            ER ( template_load_file (req->filename, (DocumentPtr) closure, NULL, FALSE) );
            ER ( document_modified ((DocumentPtr) closure, !newfile) );
            break;
        case Template:
            dprintf("HIT\n");
            if ((ResourcePtr) closure == palette)
                return NULL;
            ER ( template_load_file (req->filename, NULL, (ResourcePtr) closure,
                                     datasaveackref == req->header.yourref && claimwindow == req->windowhandle) );
            ER ( document_modified (((ResourcePtr) closure)->owner, TRUE) );
            break;
        default:
            return NULL;
        }
    }

    if (strcmp(req->filename, "<Wimp$Scrap>") == 0)
        system("%%delete <Wimp$Scrap>");

    req->header.yourref = req->header.myref;
    req->header.messageid = MESSAGE_DATA_LOAD_ACK;
    return swi (Wimp_SendMessage,  NONX,
                R0, EV_USER_MESSAGE,
                R1, req,
                R2, req->header.taskhandle,  END);
}


/*
 * Respond to a DataOpen message from the Filer.
 */

static error * dataopen_message (MessageDataLoadPtr req)
{
    DocumentPtr newdoc;
    DocumentPtr olddoc = document_lookup_by_filename (req->filename);

    if (req->filetype != FILETYPE_TEMPLATE)
        return NULL;

    /* Ack this message */
    req->header.yourref = req->header.myref;
    req->header.messageid = MESSAGE_DATA_LOAD_ACK;
    (void) swi (Wimp_SendMessage,  NONX,
                R0, EV_USER_MESSAGE,
                R1, req,
                R2, req->header.taskhandle,  END);

    if (olddoc)
    {
        document_raise_window (olddoc);
        return NULL;
    }

    /* Load to a new document.  Really oughtn't open window till we finish operation */

    ER ( document_create(&newdoc, req->filename) );
    ER ( template_load_file (req->filename, newdoc, NULL, FALSE) );
    return document_modified (newdoc, FALSE);
}


/*
 * Respond to a Save Desktop message from the Filer.
 */

static error * putbytes (const char *str, const int handle)
{
    return swi (OS_GBPB,  R0, 2,  R1, handle,  R2, str,  R3, strlen (str),  END);
}


static error * savedesktop_message (MessageSaveDesktopPtr req)
{
    error *err = NULL;
    char *command = getenv (APPDIR);

    if (command == NULL)
        return NULL;    

    EG ( fail, putbytes ("Run ", req->filehandle) );
    EG ( fail, putbytes (command, req->filehandle) );
    EG ( fail, putbytes ("\n", req->filehandle) );

fail:
    if (err)
    {
        /* Some error occurred; ack the message */
        req->header.yourref = req->header.myref;
        ER ( swi (Wimp_SendMessage,  R0, EV_USER_MESSAGE_ACKNOWLEDGE,  R1, req,  R2, req->header.taskhandle,  END) );
    }
    
    return err;
}


/*
 * Respond to all message events.  Rather annoyingly, we can't determine the
 * window/icon pair without reference to the message type, so the test
 * for the type is here rather than in the window-specific procs.
 */

static error * message (int *buf)
{
    MessageHeaderPtr hdr = (MessageHeaderPtr) buf;

    switch (hdr->messageid)
    {
    case MESSAGE_PREQUIT:
        return warn_exit ((MessagePreQuitPtr) buf);
    case MESSAGE_QUIT:
        error_exit (0);
        /* NOTREACHED */
    case MESSAGE_HELP_REQUEST:
        return help_message ((MessageHelpRequestPtr) buf);
    case MESSAGE_DRAGGING:
        return dragdrop_message_dragging ((MessageDraggingPtr) buf);
    case MESSAGE_DATA_LOAD:
        return dataload_message ((MessageDataLoadPtr) buf);
    case MESSAGE_DATA_SAVE:
        return datasave_message ((MessageDataSavePtr) buf);
    case MESSAGE_MODE_CHANGE:
        return examine_mode (TRUE);
    case MESSAGE_DATA_OPEN:
        return dataopen_message ((MessageDataLoadPtr) buf);
    case MESSAGE_SAVE_DESKTOP:
        return savedesktop_message ((MessageSaveDesktopPtr) buf);
    case MESSAGE_PALETTECHANGE:
        colours_palette ();
        props_palette();
        grid_palette();
    }
    return NULL;
}


/*
 *Install iconbar sprite
 */

static error * install_iconbar_sprite()
{
    int sx, sy, sm, px, py;
    CreateIconRec new;

    ER ( swi (Wimp_SpriteOp,  R0, 40,  R2, ICONNAME,
              OUT,   R3, &sx,  R4, &sy,  R6, &sm,   END) );

    ER ( swi (OS_ReadModeVariable,  R0, sm,  R1, 4,
              OUT,   R2, &px,   END) );

    ER ( swi (OS_ReadModeVariable,  R0, sm,  R1, 5,
              OUT,   R2, &py,   END) );

    sx <<= px; sy <<= py;

    new.windowhandle = -1;              /* App icon */
    new.icon.bbox.minx = 0;             /* Min x */
    new.icon.bbox.miny = 0;             /* Min y */
    new.icon.bbox.maxx = sx;            /* Max x */
    new.icon.bbox.maxy = sy;            /* Max y */
    new.icon.flags = IF_SPRITE | IF_HCENT | IF_INDIR |
        IF_FIELD(TYPE, 3) | IF_FIELD(FG, 7) | IF_FIELD(BG, 1);
    new.icon.data[0] = (int) ICONNAME;
    new.icon.data[1] = 1;               /* WIMP sprite area */
    new.icon.data[2] = strlen(ICONNAME);

    ER ( swi (Wimp_CreateIcon,  R1, &new,
              OUT,   R0, &iconhandle,  END) );
    return NULL;
}


/*
 * Crank up the Wimp
 */

static error * start_wimp ()
{
    int task;
    error *err;
    char *taskname = message_lookup(&msgs, "TaskName");
    if (taskname == NULL)
        taskname = ICONNAME;            /* fallback */
    strncpy((char *)&task, "TASK", 4);
    err = swi (Wimp_Initialise,
               R0, 200,         /* XXX 300! */
               R1, task,
               R2, taskname,
               OUT,
               R1, &taskhandle,  END);
    if (err == NULL)
        wimpstarted = TRUE;
    return err;
}


/* 
 * Read in window prototypes for this module and others.
 */

static error * load_prototypes (void)
{
    ER ( swi(Wimp_OpenTemplate, R1, "<" APPDIR ">.Templates", END) );
    
    ER ( wimp_load_template("ExitWarn", &exitwarnwin) );

    ER ( wimp_load_template("Info", &infowin) );
    ER ( swi (Wimp_CreateWindow,  R1, &infowin->visarea,
              OUT,  R0, &infowin->handle,  END) );
    ER ( dbox_setstring (infowin, I_INFO_VERSION, version_string() ) );
    ER ( registry_register_window (infowin->handle, Info, (void *) infowin) );
    
    ER ( document_load_prototypes() );
    ER ( template_load_prototypes() );
    ER ( winflags_load_prototypes() );
    ER ( extent_load_prototypes () );
    ER ( grid_load_prototypes() );
    ER ( props_load_prototypes() );
    ER ( saveas_load_prototypes() );
    ER ( colours_load_prototypes() );
    ER ( swi(Wimp_CloseTemplate, END) );
    return NULL;
}


/*
 * Respond to any open window request events
 */

static error * open_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {
    case Document:
        return document_open_window (win, (DocumentPtr)closure);
    case Template:
        return template_open_window (win, (ResourcePtr)closure);
    case Winflags:
        return winflags_open_window (win);
    case Grid:
        return grid_open_window (win);
    case Extent:
        return extent_open_window (win);
    case Props:
    case MoreProps:
        return props_open_window (win, type, closure);
        break;
    case Colours:
        return colours_open_window (win);
    case Info:
        infowin->visarea = win->visarea;
        infowin->scrolloffset = win->scrolloffset;
        infowin->behind = win->behind;
        return swi (Wimp_OpenWindow, R1, infowin, END);
    }
    return NULL;
}


/*
 * Respond to any close window request events
 */

static error * close_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {
    case Document:
        return document_close_window ((DocumentPtr)closure);
    case Template:
        return template_close_window ((ResourcePtr)closure);
    case Winflags:
        return winflags_close_window ();
    case Grid:
        return grid_close_window ();
    case Extent:
        return extent_close_window ();
    case Colours:
        return colours_close_window ();
#if 0
    default:
        return error_lookup("UnkWin", "CLOSEREQ");
#endif
    }
    return NULL;
}


/*
 * Respond to any redraw window request events
 */

static error * redraw_window_request (int *buf)
{
    WindowRedrawPtr redraw = (WindowRedrawPtr) buf;
    void *closure;
    RegistryType type = registry_lookup_window(redraw->handle, &closure);
    
    switch (type)
    {
    case Document:
        ER ( document_redraw_window (redraw, (DocumentPtr)closure, NULL) );
        break;
    case Template:
        ER ( template_redraw_window (redraw, (ResourcePtr)closure) );
        break;
#if 0
    case Unknown:
    default:
        return error_lookup("UnkWin", "REDRAW");
        break;
#endif
    }
    return NULL;
}


static void usage ()
{
    fprintf (stderr, "usage: " ICONNAME " [-h<pattern>] filename\n");
    error_exit (0);
}


/*
 * Main
 */

int main (int argc, char **argv)
{
    char *initialfile = NULL;
    char *pattern = NULL;
    int buf[64];

    while (argc > 1)
    {
        if (argv[1][0] == '-')
        {
            if (argv[1][1] == 'h')
                pattern = argv[1] + 2;
            else
                usage ();
        }
        else
        {
            if (initialfile)
                usage ();
            else
                initialfile = argv[1];
        }
        argc--; argv++;
    }
            
    EE ( examine_mode(FALSE) );
    EE ( message_openfile (&msgs, "<" APPDIR ">.Messages", 256) );

    if (pattern)
    {
        DocumentPtr doc;
        EE ( document_create (&doc, NULL) );
        EE ( template_load_file (initialfile, doc, NULL, FALSE) );
        EE ( genheader_only (stdout, doc, pattern) );
        error_exit (0);
    }

    EE ( start_wimp () );
    EE ( install_iconbar_sprite() );
    EE ( load_prototypes () );
    EE ( create_iconmenu () );

    /* Load initial file given on command line, if any */

    if (initialfile)
    {
        DocumentPtr newdoc;
        EE ( document_create(&newdoc, initialfile) );
        EE ( template_load_file (initialfile, newdoc, NULL, FALSE) );
        EE ( document_modified (newdoc, FALSE) );
    }


    /* Main loop */

    while (1)
    {
        int event;
        Bool consumed;
        unsigned int mask;

        mask = eventmask & interactor_event_mask();

        if (mask & BIT(EV_NULL_REASON_CODE))      /* NULL events disabled */
        {
            EE ( swi (Wimp_Poll,  R0, mask,  R1, buf,  OUT,  R0, &event,  END) );
        }
        else
        {
            int after;
            EE ( swi (OS_ReadMonotonicTime,  OUT,  R0, &after,  END) );
            after += interactor_timeout();
            EE ( swi (Wimp_PollIdle,  R0, mask,  R1, buf,  R2, after,  OUT,  R0, &event,  END) );
        }

        ED ( interactor_event (event, buf, &consumed) );
        if (!consumed)
            switch (event)
            {
            case EV_REDRAW_WINDOW_REQUEST:
                ED ( redraw_window_request (buf) );
                break;
            case EV_OPEN_WINDOW_REQUEST:
                ED ( open_window_request (buf) );
                break;
            case EV_CLOSE_WINDOW_REQUEST:
                ED ( close_window_request (buf) );
                break;
            case EV_MOUSE_CLICK:
                ED ( mouse_click(buf) );
                break ;
            case EV_KEY_PRESSED:
                ED ( key_pressed(buf) );
                break;
            case EV_LOSE_CARET:
                ED ( lose_caret(buf) );
                break;
            case EV_USER_MESSAGE:
            case EV_USER_MESSAGE_RECORDED:
                ED ( message(buf) );
                break;
            }
    }
}
